#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include "kfifo.h"

#define handle_error_en(en, msg) \
		do { errno = en; perror(msg); exit(EXIT_FAILURE); } while(0)
#define handle_error(msg) \
		do { perror(msg); exit(EXIT_FAILURE);  } while(0)

struct kfifo *fifo = NULL;
unsigned char *pBuf = NULL;

#define BUF_MAX_LEN		1024
#define STACK_MAX_SIZE	(64 * 1024)

typedef struct thread_info {
	pthread_t thread_id;
	int thread_num;
} thread_info_t;

static void sub_thread_eventloop(void *arg)
{
	thread_info_t *tinfo = arg;
	int count = 0;

	printf("Thread %d: top of stack near %p\n",
			tinfo->thread_num, &tinfo);
	while (1) {
		unsigned char buf[128];
		int len = 0;
		if (tinfo->thread_num == 1) {
			memset(buf, 0, sizeof(buf));
			snprintf((char *)buf, sizeof(buf) - 1, "count = %d\n", count++);
			len = kfifo_put(fifo, buf, strlen((char *)buf));
			printf("thread [%d] write [%d]: %s\n", tinfo->thread_num, len, buf);
			usleep(200*1000);
		} else {
			memset(buf, 0, sizeof(buf));
			len = kfifo_get(fifo, buf, sizeof(buf) - 1);
			printf("thread [%d] read [%d]: %s\n", tinfo->thread_num, len, buf);
			usleep(200*1000);
		}
	}
}

int main(void)
{
	thread_info_t tinfo[2];
	pthread_attr_t	attr;
	int ret;

	pBuf = malloc(BUF_MAX_LEN);
	if (pBuf == NULL)
		return -1;
	memset(pBuf, 0, BUF_MAX_LEN);
	fifo = kfifo_init(pBuf, BUF_MAX_LEN);
	ret = pthread_attr_init(&attr);
	if (ret != 0) {
		handle_error_en(ret, "pthread_attr_init");
		return ret;
	}
	ret = pthread_attr_setstacksize(&attr, STACK_MAX_SIZE);
	if (ret != 0) {
		handle_error_en(ret, "pthread_attr_setstacksize");
		return ret;
	}
	pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
	for (int i = 0; i < 2; i++) {
		tinfo[i].thread_num = i + 1;
		ret = pthread_create(&tinfo[i].thread_id, &attr, 
				(void *(*)(void *))&sub_thread_eventloop, &tinfo[i]);
		if (ret != 0)
			printf("Err: create thread [%d] failure!\n", i+1);
	}
	pthread_attr_destroy(&attr);
	while (1) {
		unsigned int in, out;
		int len;
		kfifo_get_in_out(fifo, &in, &out);
		printf("FIFO: in = %d, out = %d\n", in, out);
		len = kfifo_get_rest_data_length(fifo);
		printf("FIFO: rest data len = %d\n", len);
		sleep(3);
	}
	kfifo_free(fifo);

	return 0;
}